//
//  Chats.m
//  SolarSeek
//
//  Created by Erick Fejta on 05/05/21.
//  Copyright 2005 __MyCompanyName__. All rights reserved.
//

#import "Chats.h"
#import "Peers.h"

@implementation Room 

- (id)initWithName:(NSString *)n {
	return [self initWithName:n count:0];
}

- (id)initWithName:(NSString *)n count:(unsigned)count {
	if(self = [super init] ) {
		name = [n copy];
		countOfPeers = count;
		inside = NO;
		joined=NO;
	}
	return self;
}

- (void)dealloc {
	[name release];
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone {
	return [self retain];
}

+ (id)roomNamed:(NSString *)name { return [[[Room alloc] initWithName:name count:0] autorelease]; }

- (unsigned)countOfPeers { return countOfPeers; }
- (void)setCountOfPeers:(unsigned)count { countOfPeers = count; }

- (BOOL)inside { return inside; }
- (void)setInside:(BOOL)i { inside = i; }

- (BOOL)joined { return joined; }
- (void)setJoined:(BOOL)j {joined = j; }

- (NSString *)name { return name; }

- (unsigned)hash { return [name hash]; }
- (BOOL)isEqual:(id)object { 
	 BOOL b1 = [object isKindOfClass:[Room class]];
	 return b1 && [name isEqualToString:[object name]]; 
}

- (NSString *)description { return name; }

@end

@implementation Statement

- (id)initWithStatement:(NSString *)s peer:(Peer *)p date:(NSDate *)d {
	if(self = [super init]) {
		statement = [s copy];
		peer = [p retain];
		date = [d retain];
	}
	return self;
}

- (void)dealloc {
	[statement release];
	[peer release];
	[date release];
	[super dealloc];
}

+ (id)statement:(NSString *)s fromPeer:(Peer *)p {
	return [[[Statement alloc] initWithStatement:s peer:p date:[NSDate date]] autorelease];
}


- (NSString *)statement { return statement; }
- (Peer *)peer { return peer; }
- (NSDate *)date { return date; }
- (NSTimeInterval)timeElapsedTillNow { return [[NSDate date] timeIntervalSinceDate:date]; }

@end 


@implementation Chat
- (id)init { 
	if(self = [super init]) {
		type = ChatTypeNone;
		statementLast = nil;
		statements = [[NSMutableArray alloc] initWithCapacity:50];
		peers = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		peersJoined = [[KVMutableSet alloc] initWithDefaultDelay:0.05];
		waiting = [[NSMutableArray alloc] initWithCapacity:5];
		managed = YES;
		countOfStatementsMax = 1000;
		countOfStatementsNew = 0;
	}
	return self;
}

- (void)dealloc {
	[statementLast release];
	statementLast = nil;
	[statements release];
	[peers release];
	[peersJoined release];
	[waiting release];
	[super dealloc]; 
}

- (id)copyWithZone:(NSZone *)zone {
	return [self retain];
}


+ (id)chat { return [[[Chat alloc] init] autorelease]; }

- (unsigned)countOfStatementsMax { return countOfStatementsMax; }

- (BOOL)isManaged { return managed; }	

- (NSImage *)icon { return [NSImage imageNamed:@"sourcelistChat"]; }

- (KVMutableSet *)peers { return peers; }
- (KVMutableSet *)peersJoined { return peersJoined; }

- (NSArray *)statements { return statements; }
- (Statement *)statementLast { return statementLast; }
- (void)setStatementLast:(Statement *)s { [s retain]; [statementLast release]; statementLast = s; }
- (unsigned)countOfStatementsNew { return countOfStatementsNew; }
- (void)setCountOfStatementsNew:(unsigned)count { countOfStatementsNew = count; }
- (unsigned)countOfStatements { return [statements count]; }
- (id)objectInStatementsAtIndex:(unsigned)idx { return [statements objectAtIndex:idx]; }
- (void)insertObject:(id)o inStatementsAtIndex:(unsigned)idx { [statements insertObject:o atIndex:idx]; }
- (void)removeObjectFromStatementsAtIndex:(unsigned)idx { [statements removeObjectAtIndex:idx]; }

- (ChatType)type { return type; }


- (void)setCountOfStatementsMax:(unsigned)max { 
	countOfStatementsMax = max; 
	if(managed) [self trimToCountOfStatements:countOfStatementsMax];
}


- (void)setManaged:(BOOL)manage { managed = manage; }

#pragma mark actions


- (void)add {
	//add the new *waiting* statements to the *statements* array.  KVO compliant.
	//add any peers from the *waiting* statements not already in *peers*
	//	to both *peers* and *peersArray*. KVO compliant.
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(add) object:nil];
	NSArray *a = [NSArray arrayWithArray:waiting];
	[waiting removeAllObjects];	
	if([a count]) {
		NSMutableSet *newPeers = [Peer peersInCollection:waiting];
		[self willChangeValueForKey:@"countOfStatements"];
		[[self mutableArrayValueForKey:@"statements"] addObjectsFromArray:a]; //kvo add
		[self setCountOfStatementsNew:[self countOfStatementsNew]+[a count]];
		[self didChangeValueForKey:@"countOfStatements"];
		[self setStatementLast:[statements lastObject]];
		[[peers mutableSet] unionSet:newPeers];
		if(managed) {
			[self trimToCountOfStatements:countOfStatementsMax];
		}
	}
}

- (void)addStatement:(NSString *)statement fromPeer:(Peer *)peer {
	Statement *s = [Statement statement:statement fromPeer:peer];
	[waiting addObject:s];
	[self performSelector:@selector(add) withObject:nil afterDelay:0.05];
}

- (void)joinPeer:(Peer *)peer { [[peersJoined mutableSet] addObject:peer]; }
- (void)partPeer:(Peer *)peer { [[peersJoined mutableSet] removeObject:peer]; }


- (void)trimToCountOfStatements:(unsigned)count {
	//remove enough earliest messages such that the *countOfStatements* does not exceed count.
	//also remove any peers that were deleted in the statements if they are not in any other statements.
	int remove = [self countOfStatements] - count;
	if(remove > 0) {
		NSRange r = NSMakeRange(0,remove);
		NSMutableSet *peersdeleted = [Peer peersInCollection:[[self statements] subarrayWithRange:r]];
		[[self mutableArrayValueForKey:@"statements"] removeObjectsInRange:r];
		NSSet *peersremaining = [Peer peersInCollection:[self statements]];
		[peersdeleted minusSet:peersremaining];
		[[peers mutableSet] minusSet:peersdeleted];
	}
}

- (NSString *)name { return [self description]; }
	
@end

@implementation RoomChat 

- (id)initWithRoom:(Room *)r {
	if(self = [super init]) {
		type = ChatTypeRoom;
		room = [r retain];
		peerJoined = nil;
		peerParted = nil;
	}
	return self;
}

- (void)dealloc { 
	[room release];
	[peerJoined release];
	peerJoined = nil;
	[peerParted release];
	peerParted = nil;
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone {
	return [self retain];
}


- (unsigned)hash { return [room hash]; }
- (BOOL)isEqual:(id)o { return [o isKindOfClass:[RoomChat class]] && [room isEqual:[o room]]; }
- (NSString *)description { return [room description]; }

+ (id)chatWithRoom:(Room *)room { [[[RoomChat alloc] initWithRoom:room] autorelease]; }

- (NSString *)name { return [room name]; }

- (void)setPeerJoined:(Peer *)peer { 
	[peer retain]; 
	[peerJoined release]; 
	peerJoined = peer; 
}

- (void)setPeerParted:(Peer *)peer { 
	[peer retain]; 
	[peerParted release]; 
	peerParted = peer; 
}

- (void)joinPeer:(Peer *)peer { 
	BOOL add;
	add = [[[self peersJoined] mutableSet] containsObject:peer]==NO;
	[super joinPeer:peer];
	if(add) {
		[room setCountOfPeers:[peersJoined count]]; 
		[self setPeerJoined:peer];
	}
}

- (void)partPeer:(Peer *)peer {
	[peer retain];	// peer might be dealloced by super, better to keep reference
	BOOL rem = [[[self peersJoined] mutableSet] containsObject:peer];
	[super partPeer:peer];
	[room setCountOfPeers:[peersJoined count]];
	if(rem)
		[self setPeerParted:peer];
	[peer release];
}


- (Room *)room { return room; }


- (Peer *)peerJoined { return peerJoined; }
- (Peer *)peerParted { return peerParted; }
@end

@implementation PrivateChat

- (id)initWithPeer:(Peer *)p {
	if(self = [super init]) {
		type = ChatTypePrivate;
		peer = [p retain];
		[[peersJoined mutableSet] addObject:peer];
		[[peers mutableSet] addObject:peer];
	}
	return self;
}

- (void)dealloc {
	[peer release];
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone {
	return [self retain];
}


+ (id)chatWithPeer:(Peer *)peer { return [[[PrivateChat alloc] initWithPeer:peer] autorelease]; }

- (NSImage *)icon { return [peer icon]; }
- (NSString *)description { return [peer description]; }
- (unsigned)hash { return [peer hash]; }
- (BOOL)isEqual:(id)o { return [o isKindOfClass:[PrivateChat class]] && [peer isEqual:[o peer]]; }

- (Peer *)peer { return peer; }

@end